#import "CocoaGlobals.h"
#import "CocoaType.h"

NSMutableDictionary* symbols = nil;
NSMutableDictionary* classes = nil;

DEFINE_SYMBOL(CocoaObject);
DEFINE_SYMBOL(False);
DEFINE_SYMBOL(Integer);
DEFINE_SYMBOL(List);
DEFINE_SYMBOL(Null);
DEFINE_SYMBOL(Real);
DEFINE_SYMBOL(Rule);
DEFINE_SYMBOL(String);
DEFINE_SYMBOL(Symbol);
DEFINE_SYMBOL(True);

void initGlobals()
{
	if(symbols == nil)
	{
		symbols = [[NSMutableDictionary alloc] initWithCapacity: 0];
		classes = [[NSMutableDictionary alloc] initWithCapacity: 0];
		
		INIT_SYMBOL(CocoaObject);
		INIT_SYMBOL(False);
		INIT_SYMBOL(Integer);
		INIT_SYMBOL(List);
		INIT_SYMBOL(Null);
		INIT_SYMBOL(Real);
		INIT_SYMBOL(Rule);
		INIT_SYMBOL(String);
		INIT_SYMBOL(Symbol);
		INIT_SYMBOL(True);
	}
}

void deallocGlobals()
{
	if(symbols != nil)
	{
		DEALLOC_SYMBOL(CocoaObject);
		DEALLOC_SYMBOL(False);
		DEALLOC_SYMBOL(Integer);
		DEALLOC_SYMBOL(List);
		DEALLOC_SYMBOL(Null);
		DEALLOC_SYMBOL(Real);
		DEALLOC_SYMBOL(Rule);
		DEALLOC_SYMBOL(String);
		DEALLOC_SYMBOL(Symbol);
		DEALLOC_SYMBOL(True);
		
		[symbols release];
		symbols = nil;
		[classes release];
		classes = nil;
	}
}

NSMutableDictionary* GetSymbols()
{
	if(symbols == nil)
		initGlobals();
	return symbols;
}

NSMutableDictionary* GetClasses()
{
	if(classes == nil)
		initGlobals();
	return classes;
}

MExpr* CLCocoaObject(id obj)
{
	if([obj isKindOfClass: [NSValue class]])
	{
		CocoaType* type = [CocoaType typeWithType: [(NSValue*) obj objCType]];
		return (type ? [type exprForValue: (NSValue*) obj] : kSymbolNull);
	}
	else
	{
		//MExpr* expr = [MExpr exprWithFunctionHead: kSymbolCocoaObject, 
		//	MInteger((int) obj), nil];
		MExpr* expr = [MExpr exprWithSymbol: [NSString stringWithFormat: @"CocoaLink`Objects`CocoaObject%ld", (long) obj]];
		[symbols setObject: obj forKey: expr];
		return expr;
	}
}

@implementation NSObject (CLNSObjectAdditions)

- (MExpr*) expr
{
	return CLCocoaObject(self);
}

- (MExpr*) uniqueExpr
{
	NSArray* keys = [GetSymbols() allKeysForObject: self];
	if(keys && [keys count] > 0)
		return [keys objectAtIndex: 0];
	else
		return [self expr];
}

- (MExpr*) toExpression
{
	if([self isKindOfClass: [NSArray class]])
	{
		NSMutableArray* parts = [NSMutableArray arrayWithCapacity: [(NSArray*) self count] + 1];
		NSEnumerator* e = [(NSArray*) self objectEnumerator];
		id obj;
		
		[parts addObject: kSymbolList];
		while((obj = [e nextObject]) != nil)
			[parts addObject: [obj toExpression]];
		
		return [MExpr exprWithFunctionParts: parts];
	}
	else if([self isKindOfClass: [NSData class]])
	{
		return [MExpr exprWithData: (NSData*) self];
	}
	else if([self isKindOfClass: [NSDate class]])
	{
		NSMutableArray* parts = [NSMutableArray arrayWithCapacity: 7];
		NSCalendarDate* date = [(NSDate*) self dateWithCalendarFormat: nil timeZone: nil];
		
		[parts addObject: kSymbolList];
		[parts addObject: MInteger([date yearOfCommonEra])];
		[parts addObject: MInteger([date monthOfYear])];
		[parts addObject: MInteger([date dayOfMonth])];
		[parts addObject: MInteger([date hourOfDay])];
		[parts addObject: MInteger([date minuteOfHour])];
		[parts addObject: MInteger([date secondOfMinute])];
		
		return [MExpr exprWithFunctionParts: parts];
	}
	else if([self isKindOfClass: [NSDictionary class]])
	{
		NSMutableArray* parts = [NSMutableArray arrayWithCapacity: 0];
		NSEnumerator* e = [[(NSDictionary*) self allKeys] objectEnumerator];
		id key;
		
		[parts addObject: kSymbolList];
		while((key = [e nextObject]) != nil)
		{
			id val = [(NSDictionary*) self objectForKey: key];
			[parts addObject:
				MFunction(kSymbolRule, [key toExpression], [val toExpression], nil)];
		}
		return [MExpr exprWithFunctionParts: parts];
	}
	else if([self isKindOfClass: [NSSet class]])
	{
		return [[(NSSet*) self allObjects] toExpression];
	}
	else if([self isKindOfClass: [NSString class]])
	{
		return MString((NSString*) self);
	}
	else if([self isKindOfClass: [NSURL class]])
	{
		return MString([(NSURL*) self absoluteString]);
	}
	else if([self isKindOfClass: [NSImage class]])
	{
		NSData* data = [(NSImage*) self TIFFRepresentation];
		if(!data)
			return CLCocoaObject(self);
		
		return MFunction(MSymbol(@"Map"),
			MSymbol(@"Show"),
			MFunction(MSymbol(@"Flatten"),
				MFunction(MSymbol(@"List"),
					MFunction(MSymbol(@"ImportString"),
						[data toExpression], 
						MString(@"TIFF"),
						nil),
					nil
					),
				nil
				),
			nil
			);
	}
	else
		// this case handles NSValue and all other CocoaObject[]s
		return CLCocoaObject(self);
}

@end

@implementation MExpr (CocoaObject)

- (id) cocoaObject
{
	//int argc;
	//if([self checkFunction: kSymbolCocoaObject length: &argc] && argc == 1)
	if([self checkHead: kSymbolSymbol])
		return [symbols objectForKey: self];
	else if([self checkHead: kSymbolString])
		return [self string];
	else if([self checkHead: kSymbolInteger])
		return [self integer];
	else if([self checkHead: kSymbolReal])
		return [self real];
	else
		return nil;
}

@end
